Notebook to test basic functionalities

Imports

In [1]:
# Settings for notebook visualization
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = 'all'
%matplotlib inline
from IPython.core.display import HTML
HTML("""<style>.output_png img {display: block;margin-left: auto;margin-right: auto;text-align: center;vertical-align: middle;} </style>""")
Out[1]:
In [2]:
# Necessary imports
import os
import numpy as np
import pandas as pd
import matplotlib as plt
import quantstats as qs
from datetime import datetime, timedelta
print("Libraries imported correctly")
Libraries imported correctly
In [3]:
os.chdir("/Users/Sergio/Documents/Master_QF/Thesis/Code/Algorithmic Strategies")
%run Functions.ipynb

Data

In [4]:
ini_equity_default = 100
commision_default = 2/130000 + 12.5/130000 #0.000111538462, around 0.011..% of the equity
In [5]:
%run Functions.ipynb

#data = get_sp500_data(from_local_file=False, save_to_file=True)
data = get_sp500_data(from_local_file=True, save_to_file=False)
data = data[['Open', 'Close']]

data['Market_daily_ret'] = data['Close'].pct_change().fillna((data['Close']-data['Open'])/data['Open'])
data = data.loc['2000':'2020' ,['Close', 'Market_daily_ret']]

data.tail()
data['Close'].plot(title='SP500', legend=True)
Out[5]:
Close Market_daily_ret
Date
2020-10-14 3488.669922 -0.006623
2020-10-15 3483.340088 -0.001528
2020-10-16 3483.810059 0.000135
2020-10-19 3426.919922 -0.016330
2020-10-20 3443.120117 0.004727
Out[5]:
<AxesSubplot:title={'center':'SP500'}, xlabel='Date'>

Backtest of two years, using 2 different combinations of MA crossover

This is useful to see the result of doing two backtests with different parameters on two different periods. Finally those periods are merged together.

This is a backtest from 2018-01-01 to 2019-12-31. Each year will have a different combination of MAs.

  • 2018: will be long all the time.
  • 2019: will use fast_ma=75 and slow_ma=200.

1st year finishes with a long position, and 2nd year starts with a neutral position. This will be useful to see again that 'Strat_position' and 'Costs' are calculated and added correctly to the overall performance.

In [6]:
date_fmt = '%Y-%m-%d'
first_day = datetime.strptime('2018-01-01', date_fmt)
last_day = datetime.strptime('2019-12-31', date_fmt)
fast_ma = 75
slow_ma = 200

Performance of each year individually:

The purpose of each plots is:

  1. See performance of Market and Long only strategy during 2018. 'Strat_position' and 'Long_only' are always 1.
  2. See performance of Market and MA (75-200) strategy during 2019. See how 'Strat_position' comes from 0 and changes to 1 on the first day.
In [7]:
%run Functions.ipynb
df = data[first_day:last_day].copy()
commision = commision_default

cols = ['Strat_daily_ret', 'Strat_position', 'Costs', 'Long_only', 'Market_cum_ret', 'Strat_cum_ret']
tmp_df = pd.DataFrame(columns=cols)

df_year_1 = df.loc[str(first_day.year)].copy()
df_year_2 = df.loc[str(last_day.year)].copy()

strategy_1 = buy_and_hold(df_year_1)
strategy_2 = ma_crossover(df_year_2, fast_ma, slow_ma)



#  Year 1
df_year_1 = backtest_print_plot(df_year_1, strategy_1, previous_position=0, commision=commision, with_legend=True)
last_position = df_year_1.loc[df_year_1.index[-2], 'Strat_position'] # Position during the last day
last_equity = df_year_1.loc[df_year_1.index[-1], 'Market_cum_ret']
# last_equity = 100

#  Year 2
df_year_2 = backtest_print_plot(df_year_2, strategy_2, strat_params=(fast_ma, slow_ma), previous_position=last_position, ini_equity=last_equity, commision=commision, with_legend=True)

# Put together both periods
tmp_df = pd.concat([df_year_1, df_year_2], axis=0)

#  Add all obtained columns in the original df
df = pd.concat([df, tmp_df[cols]], axis=1)

# To see details of the output
df[str(df_year_1.index[0].year)].head(1)
df[str(df_year_1.index[0].year)].tail(1)
#last_equity
df[str(df_year_2.index[0].year)].head(1)
df[str(df_year_2.index[0].year)].tail(1)

# df_year_1[['Strat_position', 'Long_only']].plot()
# df_year_1[['Costs']].plot()
# df_year_2[['Costs']].plot()
Strategy: buy_and_hold()
Period: 2018-01-02 to 2018-12-31
	Overall return of SP500: 93.76 %. IR of SP500: -0.37
	Overall return of strategy: 93.76 %. IR strategy: -0.37
Strategy: ma_crossover(75-200)
Period: 2019-01-02 to 2019-12-31
	Overall return of SP500: 128.88 %. IR of SP500: 2.32
	Overall return of strategy: 110.34 %. IR strategy: 1.00
Out[7]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2018-01-02 2695.810059 0.008303 0.008191 1.0 0.011154 1.0 100.830336 100.81909
Out[7]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2018-12-31 2506.850098 0.008492 0.008492 NaN 0.0 1.0 93.76274 93.752282
Out[7]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2019-01-02 2510.030029 0.001268 -0.000112 0.0 0.010471 0.0 93.881678 93.752282
Out[7]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2019-12-31 3230.780029 0.002946 0.002946 NaN 0.0 0.0 120.83961 103.462026

Performance of both time periods together

The purpose of each plots is:

  1. Show previous plots concatenated. See that 'Strat_position' and 'Long_only' change properly in the beggining of 2019
  2. Subplots to see how the changes in the strategy position affect 'Long_only', 'Strat_position', and 'Costs'
In [8]:
%run Functions.ipynb

results_df = prepare_oos_df(df.loc[first_day:last_day].copy(), commision=commision_default)
show_oos_plot(results_df, with_legend=True)

print("Period: {:%Y-%m-%d} to {:%Y-%m-%d}".format(results_df.index[0], results_df.index[-1]))
print("\tOverall return of SP500: {:.2f} %. SR of SP500: {:.2f}".format(results_df['Market_cum_ret'][-1], results_df['Market_daily_ret'].sharpe()))
print("\tOverall return of our strategy: {:.2f} %. Sharpe ratio strategy: {:.2f}".format(results_df['Strat_cum_ret'][-1], results_df['Strat_cum_ret'].sharpe()))

cols = ['Long_only', 'Strat_position', 'Costs']
results_df[cols].plot(subplots=True, title='Strategy Postition and costs (in USD)', color=('tab:brown', 'r', 'k'));
plt.show()
results_df['Costs'].plot(title='Transaction Costs in USD (with more zoom)', color=('k'));
Period: 2018-01-02 to 2019-12-31
	Overall return of SP500: 120.84 %. SR of SP500: 0.71
	Overall return of our strategy: 103.45 %. Sharpe ratio strategy: 0.16

Checking 'Strat_position' and 'Costs' of last days of year 1, and first days of year 2

The purpose of this section is to check how 'Strat_position' and 'Costs' have changed in the dataframe. More specifically, on dates:

  1. Start of backtesting period: 'Costs' to open the position for the following day and its effect on 'Strat_cum_ret' should be shown

  2. First days of 2019. Since we need to change the 'Strat_position' from long to being neutral, 'Costs' should be shown for closing the position. We can also see the change in 'Long_only' from 1 to 0.

  3. At the end of 2019-04-23, there is an upward MA cross. 'Strat_position' and 'Costs' should change accordingly.
In [9]:
def style_original_value(x):
    color = 'background-color: darkorange'
    df1 = pd.DataFrame('', index=x.index, columns=x.columns)
    df1.loc['2018-12-31', ['Strat_daily_ret', 'Strat_position', 'Costs', 'Strat_cum_ret']] = color
    df1.loc['2019-01-02', ['Strat_daily_ret', 'Costs', 'Strat_cum_ret']] = color

    return df1

def style_fixed_value(x):
    color = 'background-color: green'
    df1 = pd.DataFrame('', index=x.index, columns=x.columns)
    df1.loc['2018-12-31', ['Strat_daily_ret', 'Strat_position', 'Costs', 'Strat_cum_ret']] = color
    df1.loc['2019-01-02', ['Strat_daily_ret', 'Costs', 'Strat_cum_ret']] = color
    return df1

df['2018-12-28':'2019-01-02'].style.apply(style_original_value, axis=None)

results_df['2018-12-28':'2019-01-02'].style.apply(style_fixed_value, axis=None)
Out[9]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2018-12-28 00:00:00 2485.739990 -0.001242 -0.001242 1.000000 0.000000 1.000000 92.973167 92.962797
2018-12-31 00:00:00 2506.850098 0.008492 0.008492 nan 0.000000 1.000000 93.762740 93.752282
2019-01-02 00:00:00 2510.030029 0.001268 -0.000112 0.000000 0.010471 0.000000 93.881678 93.752282
Out[9]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2018-12-28 00:00:00 2485.739990 -0.001242 -0.001242 1.000000 0.000000 1.000000 92.973167 92.962797
2018-12-31 00:00:00 2506.850098 0.008492 0.008380 0.000000 0.010457 1.000000 93.762740 93.741825
2019-01-02 00:00:00 2510.030029 0.001268 0.000000 0.000000 0.000000 0.000000 93.881678 93.741825
In [10]:
%run Functions.ipynb
results_df = prepare_oos_df(df.loc[first_day:last_day].copy(), commision=0.005)

last_day_2018 = results_df[str(first_day.year)].index[-1]
first_day_2019 = results_df[str(last_day.year)].index[0]

# df contains the output df from both backtests concatenated
# results_df contains the "fixed" df. 

print("First two days: \n\tWe can see how we did not enter the market until the end of the first trading day.\n\t"
        "So the only returns from the first day come from transaction costs, that were added when we entered a long position\n\t"
        "Costs = $ 0.5, because commision was set to 0.005 in this test")
results_df.head(2)

print("End of first OOS period and beginning of second OOS period:\n\t"
        "Following two dataframes allow to see how the transaction costs from the first day of second year is moved to the last day of the first year.\n\t"
        "(Because we change the position right before the close of the first year)\n")

print("Original df:")
df['2018-12-28':'2019-01-03']
print("Fixed df:\n\t"
        "We can see how Strat_position and Costs were fixed properly from the original df to the fixed df:\n\t"
        "\tOn 2018-12-31: \n\t"
        "\tCosts (in USD) = previous(Strat_cum_ret) * [1+[Market_Daily_ret*previous(Strat_position)]] * commission\n\t"
        "\tCosts (in USD) = 92.508301 * (1+(0.008492*1.0)) * (0.005) = 0.46647")
results_df['2018-12-28':'2019-01-03']


print("Change of strategy position (at the end of 2019-04-24): \n\tThere is a MA crossover at the 'Close' of 2019-04-24. A change in Strat_position should be shown,"
        " with a cost to be paid at the end of the day\n\t"
        "\tOn 2019-04-24: \n\t"  
        "\tCosts (in USD) = previous(Strat_cum_ret) * [1+[Market_Daily_ret*previous(Strat_position)]] * commission\n\t"
        "\tCosts (in USD) = 92.827457 * (1+(-0.002192*0.0)) * (0.005) = 0.464137")

results_df.loc['2019-04-22':'2019-04-25']


print("Last two days. We can see information about cummulative returns from the benchmark and out strategy. \n\t"
        "We have NaN in 'Strat_position', because we have not decided a strategy position for the following OOS period\n")
#df.tail(2)
results_df.tail(2)

show_oos_plot(results_df, with_legend=True)
First two days: 
	We can see how we did not enter the market until the end of the first trading day.
	So the only returns from the first day come from transaction costs, that were added when we entered a long position
	Costs = $ 0.5, because commision was set to 0.005 in this test
Out[10]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2018-01-02 2695.810059 0.008303 0.003262 1.0 0.5 1.0 100.830336 100.326184
2018-01-03 2713.060059 0.006399 0.006399 1.0 0.0 1.0 101.475531 100.968154
End of first OOS period and beginning of second OOS period:
	Following two dataframes allow to see how the transaction costs from the first day of second year is moved to the last day of the first year.
	(Because we change the position right before the close of the first year)

Original df:
Out[10]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2018-12-28 2485.739990 -0.001242 -0.001242 1.0 0.000000 1.0 92.973167 92.962797
2018-12-31 2506.850098 0.008492 0.008492 NaN 0.000000 1.0 93.762740 93.752282
2019-01-02 2510.030029 0.001268 -0.000112 0.0 0.010471 0.0 93.881678 93.752282
2019-01-03 2447.889893 -0.024757 0.000000 0.0 0.000000 0.0 91.557475 93.752282
Fixed df:
	We can see how Strat_position and Costs were fixed properly from the original df to the fixed df:
		On 2018-12-31: 
		Costs (in USD) = previous(Strat_cum_ret) * [1+[Market_Daily_ret*previous(Strat_position)]] * commission
		Costs (in USD) = 92.508301 * (1+(0.008492*1.0)) * (0.005) = 0.46647
Out[10]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2018-12-28 2485.739990 -0.001242 -0.001242 1.0 0.00000 1.0 92.973167 92.508301
2018-12-31 2506.850098 0.008492 0.003450 0.0 0.46647 1.0 93.762740 92.827457
2019-01-02 2510.030029 0.001268 0.000000 0.0 0.00000 0.0 93.881678 92.827457
2019-01-03 2447.889893 -0.024757 0.000000 0.0 0.00000 0.0 91.557475 92.827457
Change of strategy position (at the end of 2019-04-24): 
	There is a MA crossover at the 'Close' of 2019-04-24. A change in Strat_position should be shown, with a cost to be paid at the end of the day
		On 2019-04-24: 
		Costs (in USD) = previous(Strat_cum_ret) * [1+[Market_Daily_ret*previous(Strat_position)]] * commission
		Costs (in USD) = 92.827457 * (1+(-0.002192*0.0)) * (0.005) = 0.464137
Out[10]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2019-04-22 2907.969971 0.001012 0.000000 0.0 0.000000 0.0 108.765671 92.827457
2019-04-23 2933.679932 0.008841 0.000000 0.0 0.000000 0.0 109.727291 92.827457
2019-04-24 2927.250000 -0.002192 -0.005000 1.0 0.464137 0.0 109.486794 92.363320
2019-04-25 2926.169922 -0.000369 -0.000369 1.0 0.000000 0.0 109.446397 92.329240
Last two days. We can see information about cummulative returns from the benchmark and out strategy. 
	We have NaN in 'Strat_position', because we have not decided a strategy position for the following OOS period

Out[10]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2019-12-30 3221.290039 -0.005781 -0.005781 1.0 0.0 0.0 120.48466 101.641145
2019-12-31 3230.780029 0.002946 0.002946 NaN 0.0 0.0 120.83961 101.940582

Walk forward optimization.

Walk Forward period: In sample Out of Sample 2015-01-01 : 2017-12-31 2018-01-01 : 2018-12-31 2016-01-01 : 2018-12-31 2019-01-01 : 2019-12-31 fast_ma = [1, 3, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65] slow_ma = [5, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260] Robust SR = (Individual_SR + Neighbors_average_SR) / 2
In [11]:
%run Functions.ipynb

# fast_ma_list = [1, 3, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65]
# slow_ma_list = [5, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260]

fast_ma_list = [5, 10, 15, 20, 25, 30, 500, 500]
slow_ma_list = [50, 100, 150, 175, 200, 225, 250, 300]

IS_start_years = pd.date_range(start='2015-01-01', end='2016-01-01', freq='1YS', closed=None)
IS_end_years = pd.date_range(start='2017-12-31', end='2018-12-31', freq='1Y', closed=None)
OOS_start_years = pd.date_range(start='2018-01-01', end='2019-01-01', freq='1YS', closed=None)
OOS_end_years = pd.date_range(start='2018-12-31', end='2019-12-31', freq='1Y', closed=None)

# IS_start_years = pd.date_range(start='1997-01-01', end='2017-01-01', freq='1YS', closed=None)
# IS_end_years = pd.date_range(start='1999-12-31', end='2019-12-31', freq='1Y', closed=None)
# OOS_start_years = pd.date_range(start='2000-01-01', end='2020-01-01', freq='1YS', closed=None)
# OOS_end_years = pd.date_range(start='2000-12-31', end='2020-12-31', freq='1Y', closed=None)

print_periods(IS_start_years, IS_end_years, OOS_start_years, OOS_end_years)

num_neighbors_matrix = get_num_neighbors(fast_ma_list, slow_ma_list)
	In SAMPLE		OOS
2015-01-01 : 2017-12-31 	 2018-01-01 : 2018-12-31
2016-01-01 : 2018-12-31 	 2019-01-01 : 2019-12-31
Number of periods: 2 : 2     2 : 2
In [12]:
%run Functions.ipynb
df_walk_forward = data[IS_start_years[0]:OOS_end_years[-1]].copy()
df_walk_forward = df_walk_forward.loc[:'2020-05-02']

# Copy data and create necessary columns 
new_cols = ['Strat_daily_ret', 'Strat_position', 'Long_only', 'Costs', 'Market_cum_ret']

df_walk_forward = df_walk_forward.reindex(columns = df_walk_forward.columns.tolist() + new_cols)

last_position = 0 # We suppose that we start not being invested. 1:long, -1:short

optimization_results_list = []
robust_optimization_results_list = []
market_ir_list = []

num_loop=0
for IS_start, IS_end, OOS_start, OOS_end in zip(IS_start_years, IS_end_years, OOS_start_years, OOS_end_years):
    #if num_loop == 1: break
    num_loop += 1
    
    strats_ir_matrix = np.zeros((len(fast_ma_list),len(slow_ma_list)))
    strat_pnl_matrix = np.zeros((len(fast_ma_list),len(slow_ma_list)))
    
    is_period = df_walk_forward.loc[IS_start:IS_end].copy()
    oos_period = df_walk_forward.loc[OOS_start:OOS_end].copy()
    
    ############################################ IN SAMPLE  ############################################
    strat_pnl_matrix, strats_ir_matrix, market_pnl, market_ir = run_all_combinations(is_period, fast_ma_list, slow_ma_list, last_position)
    optimization_results_list.append(strats_ir_matrix)
    market_ir_list.append(market_ir)
    
    # Get index of maximum IR for the In-Sample period, or buy_and_hold if it performed better
    fast_index, slow_index, robust_strats_ir_matrix = get_best_combination(strats_ir_matrix, market_ir, num_neighbors_matrix, allow_long_only=False)
    robust_optimization_results_list.append(robust_strats_ir_matrix)
    
    if robust_strats_ir_matrix[fast_index, slow_index] > market_ir:
        fast_ma_best = fast_ma_list[fast_index]
        slow_ma_best = slow_ma_list[slow_index]
    else: # If buy_and_hold performed better, we select buy_and_hold for the OOS
        fast_ma_best = 0
        slow_ma_best = 0
    
    print("Best In-sample performance: ")
    print_backtest_stats(is_period, strat_pnl_matrix[fast_index, slow_index], strats_ir_matrix[fast_index, slow_index],
                         market_pnl, market_ir,
                         strat_params=(fast_ma_best, slow_ma_best))
    
    show_both_heatmaps(strats_ir_matrix, robust_strats_ir_matrix, market_ir, plot_title=str(IS_start.year), x_title="Slow MA", x_values=slow_ma_list, y_title="Fast MA", y_values=fast_ma_list)    

    ############################################ OUT OF SAMPLE  ############################################
    print("OOS performance:")
    strategy = ma_crossover(oos_period, fast_ma_best, slow_ma_best)
    oos_period, last_position, strat_pnl, strat_ir, market_pnl, market_ir = backtest_strat(oos_period, strategy, previous_position=last_position)
    print_backtest_stats(oos_period, strat_pnl, strat_ir, market_pnl, market_ir,
                     strat_params=(fast_ma_best, slow_ma_best))
    
    # Save come columns from OOS
    df_walk_forward.loc[OOS_start:OOS_end, new_cols] = oos_period[new_cols]
    print()

print("End of Walk Forward Optimization")
Best In-sample performance: 
Strategy: buy_and_hold()
Period: 2015-01-02 to 2017-12-29
	Overall return of SP500: 129.86 %. IR of SP500: 0.74
	Overall return of strategy: 129.86 %. IR strategy: 0.74
OOS performance:
buy_hold
Strategy: buy_and_hold()
Period: 2018-01-02 to 2018-12-31
	Overall return of SP500: 93.76 %. IR of SP500: -0.37
	Overall return of strategy: 93.76 %. IR strategy: -0.37

Best In-sample performance: 
Strategy: ma_crossover(25-225)
Period: 2016-01-04 to 2018-12-31
	Overall return of SP500: 122.65 %. IR of SP500: 0.54
	Overall return of strategy: 137.20 %. IR strategy: 1.06
OOS performance:
Strategy: ma_crossover(25-225)
Period: 2019-01-02 to 2019-12-31
	Overall return of SP500: 128.88 %. IR of SP500: 2.32
	Overall return of strategy: 115.66 %. IR strategy: 1.42

End of Walk Forward Optimization
In [13]:
%run Functions.ipynb
ini_mon = df_walk_forward[str(OOS_start_years[0].year-1)].iloc[-1]['Close']
#ini_mon = 100
results_df = prepare_oos_df(df_walk_forward.loc[OOS_start_years[0]:OOS_end_years[-1]].copy(), ini_equity=ini_mon)
results_df.iloc[[0,-2,-1]]
show_oos_plot(results_df, with_legend=True, with_signals=True)
#plt.show()
#results_df['Costs'].cumsum().plot();
Out[13]:
Close Market_daily_ret Strat_daily_ret Strat_position Costs Long_only Market_cum_ret Strat_cum_ret
Date
2018-01-02 2695.810059 0.008303 0.008191 1.0 0.29821 1.0 2695.810059 2695.509372
2019-12-30 3221.290039 -0.005781 -0.005781 1.0 0.00000 0.0 3221.290039 2890.489960
2019-12-31 3230.780029 0.002946 0.002946 NaN 0.00000 0.0 3230.780029 2899.005406
In [14]:
%run Functions.ipynb
from ipywidgets import widgets

options = [year for year in IS_start_years.year]

selection_slider = widgets.SelectionSlider(
    options=options,
    description='Starting IS year',
    orientation='horizontal',
    layout={'width': '900px'},
)
#selection_slider

def plot_heatmap(year):
    index = year - IS_start_years.year[0]
    show_both_heatmaps(optimization_results_list[index], robust_optimization_results_list[index], market_ir_list[index], plot_title=str(year), x_title="Slow MA", x_values=slow_ma_list, y_title="Fast MA", y_values=fast_ma_list)    
    
widgets.interact(
    plot_heatmap,
    year=selection_slider
);

Calculate performance metrics

In [15]:
%run Functions.ipynb
print("Initial invested capital = {:.2f}".format(ini_mon))
results_df[['Strat_cum_ret', 'Market_cum_ret']].iloc[[0,1,-2,-1]]

metrics = calculate_performance_metrics(results_df, strat_name='MA Crossover')
metrics
show_oos_plot(results_df, with_legend=True, with_signals=True)
Initial invested capital = 2673.61
Out[15]:
Strat_cum_ret Market_cum_ret
Date
2018-01-02 2695.509372 2695.810059
2018-01-03 2712.757448 2713.060059
2019-12-30 2890.489960 3221.290039
2019-12-31 2899.005406 3230.780029
Out[15]:
AbsRet ARC IR aSD MD AMD MLD All Risk ARCMD ARCAMD Num Trades No signal
MA Crossover 8.4304 4.1383 0.2885 14.3444 19.7782 13.3072 1.2758 0.4817 0.2092 0.3110 3 42
Buy and Hold 20.8396 9.9478 0.6653 14.9513 19.7782 13.3072 0.5859 0.2306 0.5030 0.7475 1 0

Save results

After the walk-forward optimization we save the results. The most important column is 'Strat_position'. From it, we can obtain daily and cummulative returns of the strategy.

In [16]:
filename = "Test_results_ma.csv"
# results_df.to_csv("data/{}".format(filename))

if filename not in os.listdir("data"):
    results_df.to_csv("data/{}".format(filename))
    print("File saved properly")
else:
    print("File already exists")
    
File already exists